package com.idea.relax.log.aspect;

import com.idea.relax.log.api.custom.ICustomFieldsProvider;
import com.idea.relax.log.constant.LogConstant;
import com.idea.relax.log.props.RelaxLogProperties;
import com.idea.relax.log.support.LoggerUtil;
import com.idea.relax.log.support.MethodExecuteResult;
import com.idea.relax.log.support.utils.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.web.servlet.error.ErrorController;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * @author: salad
 * @date: 2022/07/10
 * @description: 请求日志的切面
 */
@Slf4j
@AllArgsConstructor
public class RequestLogAspect extends RequestLogSupport implements MethodInterceptor {

    private final RelaxLogProperties properties;

    private final ObjectProvider<ICustomFieldsProvider> objectProvider;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        RelaxLogProperties.RequestLog requestLog = properties.getRequestLog();
        HttpServletRequest request = WebUtil.getRequest();

        if (isProceed(invocation, requestLog, request)) {
            return invocation.proceed();
        }

        //目标方法执行状态上下文
        MethodExecuteResult methodExecuteResult = new MethodExecuteResult();
        LoggerUtil logger = LoggerUtil.getLogger();
        try {
            //前置方法
            handleBeforeMethod(invocation, requestLog, logger, request);
        } catch (Exception e) {
            log.error("handleBeforeMethod execution error...", e);
        }
        long time;
        Object result;
        long startNs = System.nanoTime();
        try {
            //执行目标方法
            result = invocation.proceed();
            time = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
            methodExecuteResult.setSuccess(result, time);
        } catch (Throwable ex) {
            time = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
            //异常方法
            methodExecuteResult.setError(ex, time);
            handleExMethod(methodExecuteResult, ex, properties, request);
            throw ex;
        } finally {
            try {
                //后置方法
                handleAfterMethod(logger,objectProvider,request.getMethod(),
                        Objects.requireNonNull(request).getRequestURI(), requestLog, methodExecuteResult);
            } catch (Exception e) {
                log.error("handleAfterMethod execution error...", e);
            }
            logger.clear();
            if (properties.getRequestLog().isAddRichFields()) {
                MDCUtil.removes(LogConstant.REQ_MDC_FIELDS);
                MDCUtil.removes(LogConstant.RESP_MDC_FIELDS);
                MDCUtil.removes(LogConstant.ERR_MDC_FIELDS);
                MDCUtil.removes(LogConstant.SERVICE_MDC_FIELDS);
            }
        }
        return result;
    }

    private boolean isProceed(MethodInvocation invocation, RelaxLogProperties.RequestLog requestLog,
                              HttpServletRequest request) {
    	//TODO HandlerMethodArgumentResolver无法抛出
        return null == request || !requestLog.isEnabled() || isErrorController(invocation.getThis());
    }

    /**
     * 判断是否是SpringBoot自带的ErrorController控制器以及其子类
     *
     * @param target
     * @return
     */
    private boolean isErrorController(Object target) {
        return ErrorController.class
                .isAssignableFrom(Objects.requireNonNull(target).getClass());
    }

}
